/**
 * @brief MCU GPIO 驱动适配层
 * @author 张勇 / 2020-05-13
 */
 
#include <stdint.h>

#include <stm32f4xx.h>
#include <stm32f4xx_ll_bus.h>
#include <stm32f4xx_ll_gpio.h>
#include <stm32f4xx_ll_exti.h>

#include "share/cccompat.h"
#include "share/utils.h"
#include "mcu.h"
#include "_mcu_gpio.h"

GPIO_TypeDef* mcu_gpio_port(mcu_gpio_num_t num) {
	if(num <= MCU_GPIO_NUM_PA15) { return GPIOA; }
	if(num <= MCU_GPIO_NUM_PB15) { return GPIOB; }
	if(num <= MCU_GPIO_NUM_PC15) { return GPIOC; }
	if(num <= MCU_GPIO_NUM_PD15) { return GPIOD; }
	if(num <= MCU_GPIO_NUM_PE15) { return GPIOE; }
	if(num <= MCU_GPIO_NUM_PF15) { return GPIOF; }

#ifdef GPIOG
	if(num <= MCU_GPIO_NUM_PG15) { return GPIOG; }
#endif
#ifdef GPIOH
	if(num <= MCU_GPIO_NUM_PH15) { return GPIOH; }
#endif
#ifdef GPIOI
	if(num <= MCU_GPIO_NUM_PI15) { return GPIOI; }
#endif
#ifdef GPIOJ
	if(num <= MCU_GPIO_NUM_PJ15) { return GPIOJ; }
#endif
#ifdef GPIOK
	if(num <= MCU_GPIO_NUM_PK15) { return GPIOK; }
#endif
	return NULL;
}

// mcu_gpio_num_t 转换为 LL_GPIO_PIN_x
uint32_t mcu_gpio_pin(mcu_gpio_num_t num) {
	return 1UL << (num & 0x0F);
}

// mcu_gpio_num_t 转换为 LL_EXTI_LINE_x
static inline uint32_t _gpio_exti_line(mcu_gpio_num_t num) {
	return 1UL << (num & 0x0F);
}

// 使能 GPIO 端口总线时钟
/*
static std_err_t _gpio_port_clock_enable(GPIO_TypeDef* port, bool enable) {
	if(enable) {
		switch((uintptr_t)port) {
			case (uintptr_t)GPIOA: LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA); break;
			case (uintptr_t)GPIOB: LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOB); break;
			case (uintptr_t)GPIOC: LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOC); break;
			case (uintptr_t)GPIOD: LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOD); break;
			case (uintptr_t)GPIOE: LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOE); break;
			case (uintptr_t)GPIOF: LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOF); break;
			case (uintptr_t)GPIOG: LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOG); break;
			case (uintptr_t)GPIOH: LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOH); break;
			case (uintptr_t)GPIOI: LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOI); break;
			default: return STD_ERR_INVALID_ARG;
		}
	} else {
		switch((uintptr_t)port) {
			case (uintptr_t)GPIOA: LL_AHB1_GRP1_DisableClock(LL_AHB1_GRP1_PERIPH_GPIOA); break;
			case (uintptr_t)GPIOB: LL_AHB1_GRP1_DisableClock(LL_AHB1_GRP1_PERIPH_GPIOB); break;
			case (uintptr_t)GPIOC: LL_AHB1_GRP1_DisableClock(LL_AHB1_GRP1_PERIPH_GPIOC); break;
			case (uintptr_t)GPIOD: LL_AHB1_GRP1_DisableClock(LL_AHB1_GRP1_PERIPH_GPIOD); break;
			case (uintptr_t)GPIOE: LL_AHB1_GRP1_DisableClock(LL_AHB1_GRP1_PERIPH_GPIOE); break;
			case (uintptr_t)GPIOF: LL_AHB1_GRP1_DisableClock(LL_AHB1_GRP1_PERIPH_GPIOF); break;
			case (uintptr_t)GPIOG: LL_AHB1_GRP1_DisableClock(LL_AHB1_GRP1_PERIPH_GPIOG); break;
			case (uintptr_t)GPIOH: LL_AHB1_GRP1_DisableClock(LL_AHB1_GRP1_PERIPH_GPIOH); break;
			case (uintptr_t)GPIOI: LL_AHB1_GRP1_DisableClock(LL_AHB1_GRP1_PERIPH_GPIOI); break;
			default: return STD_ERR_INVALID_ARG;
		}
	}
	return STD_ERR_OK;
}
*/

int mcu_gpio_exti_irqn(mcu_gpio_num_t num) {
	uint32_t pin = num & 0x0F;
	if(pin ==  0) { return EXTI0_IRQn;     }
	if(pin ==  1) { return EXTI1_IRQn;     }
	if(pin ==  2) { return EXTI2_IRQn;     }
	if(pin ==  3) { return EXTI3_IRQn;     }
	if(pin ==  4) { return EXTI4_IRQn;     }
	if(pin <=  9) { return EXTI9_5_IRQn;   }
	return EXTI15_10_IRQn;
}

std_err_t mcu_gpio_pinmux_config(mcu_gpio_num_t num, uint32_t func) {
	if(num >= MCU_GPIO_NUM_MAX || func > 15) return STD_ERR_INVALID_ARG;
	GPIO_TypeDef* port = mcu_gpio_port(num);
	uint32_t pin = mcu_gpio_pin(num);
    pin <= LL_GPIO_PIN_7 ? LL_GPIO_SetAFPin_0_7(port, pin, func) : LL_GPIO_SetAFPin_8_15(port, pin, func);
	return STD_ERR_OK;
}

std_err_t mcu_gpio_config(mcu_gpio_num_t num, mcu_gpio_mode_t mode) {
	if(num >= MCU_GPIO_NUM_MAX) return STD_ERR_INVALID_ARG;
	GPIO_TypeDef* port = mcu_gpio_port(num);
	uint16_t pin = mcu_gpio_pin(num);
	
	// 使能 GPIO 端口总线时钟
	//_gpio_port_clock_enable(port, true);
	
	if(mode == MCU_GPIO_MODE_INPUT || mode == MCU_GPIO_MODE_INPUT_FLOAT || mode == MCU_GPIO_MODE_INPUT_PU || mode == MCU_GPIO_MODE_INPUT_PD) {
		LL_GPIO_SetPinMode(port, pin, LL_GPIO_MODE_INPUT);
		switch(mode) {
			case MCU_GPIO_MODE_INPUT_PU: LL_GPIO_SetPinPull(port, pin, LL_GPIO_PULL_UP);   break;
			case MCU_GPIO_MODE_INPUT_PD: LL_GPIO_SetPinPull(port, pin, LL_GPIO_PULL_DOWN); break;
			default:                     LL_GPIO_SetPinPull(port, pin, LL_GPIO_PULL_NO);   break;
		}
	} else if(mode == MCU_GPIO_MODE_OUTPUT || mode == MCU_GPIO_MODE_OUTPUT_PP || mode == MCU_GPIO_MODE_OUTPUT_OD) {
		// 设置为输出模式
		LL_GPIO_SetPinMode(port, pin, LL_GPIO_MODE_OUTPUT);
		// 设置输出驱动能力
		switch(mode) {
			case MCU_GPIO_MODE_OUTPUT_OD: LL_GPIO_SetPinOutputType(port, pin, LL_GPIO_OUTPUT_OPENDRAIN); LL_GPIO_SetPinPull(port, pin, LL_GPIO_PULL_NO); break;
			default:                      LL_GPIO_SetPinOutputType(port, pin, LL_GPIO_OUTPUT_PUSHPULL ); break;
		}
		// 设置输出速度
		LL_GPIO_SetPinSpeed(GPIOB, LL_GPIO_PIN_6, LL_GPIO_SPEED_FREQ_HIGH);
	} else {
		return STD_ERR_INVALID_ARG;
	}
	
	return STD_ERR_OK;
}

int8_t mcu_gpio_read_in(mcu_gpio_num_t num) {
	if(num >= MCU_GPIO_NUM_MAX) return -1;
	GPIO_TypeDef* port = mcu_gpio_port(num);
	uint32_t pin = mcu_gpio_pin(num);
	return ((port->IDR & pin) != 0);
}

int8_t mcu_gpio_read_out(mcu_gpio_num_t num) {
	if(num >= MCU_GPIO_NUM_MAX) return -1;
	GPIO_TypeDef* port = mcu_gpio_port(num);
	uint32_t pin = mcu_gpio_pin(num);
	return ((port->ODR & pin) != 0);
}

std_err_t mcu_gpio_write_out(mcu_gpio_num_t num, bool level) {
	if(num >= MCU_GPIO_NUM_MAX) return STD_ERR_INVALID_ARG;
	GPIO_TypeDef* port = mcu_gpio_port(num);
	uint32_t pin = mcu_gpio_pin(num);
	if(level) {
		port->ODR |= pin;
	} else {
		port->ODR &= (~pin);
	}
	return STD_ERR_OK;
}

std_err_t mcu_gpio_toggle(mcu_gpio_num_t num) {
	if(num >= MCU_GPIO_NUM_MAX) return STD_ERR_INVALID_ARG;
	GPIO_TypeDef* port = mcu_gpio_port(num);
	uint32_t pin = mcu_gpio_pin(num);
	uint32_t val = port->ODR;
	port->ODR = val ^ pin;
	return STD_ERR_OK;
}

int8_t mcu_gpio_bidi_read_in(mcu_gpio_num_t num, mcu_gpio_mode_t in_mode) {
	if(num >= MCU_GPIO_NUM_MAX) return STD_ERR_INVALID_ARG;
	GPIO_TypeDef* port = mcu_gpio_port(num);
	uint16_t pin = mcu_gpio_pin(num);
	// 如果当前不是输入模式，则设置为输入模式
	if(LL_GPIO_GetPinMode(port, pin) != LL_GPIO_MODE_INPUT) mcu_gpio_config(num, in_mode);
	return ((port->IDR & pin) != 0);
}

std_err_t mcu_gpio_bidi_write_out(mcu_gpio_num_t num, mcu_gpio_mode_t out_mode, bool level) {
	if(num >= MCU_GPIO_NUM_MAX) return STD_ERR_INVALID_ARG;
	GPIO_TypeDef* port = mcu_gpio_port(num);
	uint16_t pin = mcu_gpio_pin(num);
	
	// 注意: 要先设置输出寄存器，再根据情况设置为输出模式，确保设置为输出模式时立即输出目标电平 ! 如果颠倒顺序，可能会导致输出电平有毛刺 !!!
	if(level) {
		port->ODR |= pin;
	} else {
		port->ODR &= (~pin);
	}
	
	// 如果当前不是输出模式，则设置为输出模式
	if(LL_GPIO_GetPinMode(port, pin) != LL_GPIO_MODE_OUTPUT) mcu_gpio_config(num, out_mode);
	return STD_ERR_OK;
}

// 默认的 EXTI 中断处理
_RAMCODE_ void EXTI0_IRQHandler(void)     { mcu_call_irq_handlers(EXTI0_IRQn);     LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_0); }
_RAMCODE_ void EXTI1_IRQHandler(void)     { mcu_call_irq_handlers(EXTI1_IRQn);     LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_1); }
_RAMCODE_ void EXTI2_IRQHandler(void)     { mcu_call_irq_handlers(EXTI2_IRQn);     LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_2); }
_RAMCODE_ void EXTI3_IRQHandler(void)     { mcu_call_irq_handlers(EXTI3_IRQn);     LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_3); }
_RAMCODE_ void EXTI4_IRQHandler(void)     { mcu_call_irq_handlers(EXTI4_IRQn);     LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_4); }
_RAMCODE_ void EXTI9_5_IRQHandler(void)   { mcu_call_irq_handlers(EXTI9_5_IRQn);   LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_9 | LL_EXTI_LINE_8 | LL_EXTI_LINE_7 | LL_EXTI_LINE_6 | LL_EXTI_LINE_5); }
_RAMCODE_ void EXTI15_10_IRQHandler(void) { mcu_call_irq_handlers(EXTI15_10_IRQn); LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_15 | LL_EXTI_LINE_14 | LL_EXTI_LINE_13 | LL_EXTI_LINE_12 | LL_EXTI_LINE_11 | LL_EXTI_LINE_10); }

std_err_t mcu_gpio_exti_config(mcu_gpio_num_t num, mcu_gpio_intr_type_t trig, void *opt, mcu_isr_t isr, void *isr_cookie) {
	return STD_ERR_OK;
}

std_err_t mcu_gpio_exti_toggle_trig_polarity(mcu_gpio_num_t num) {
	return STD_ERR_OK;
}

std_err_t mcu_gpio_exti_enable(mcu_gpio_num_t num, bool enable) {
	return STD_ERR_OK;
}

uint32_t mcu_gpio_exti_flag(mcu_gpio_num_t num) {
	uint32_t extiLine = _gpio_exti_line(num);
	return LL_EXTI_IsActiveFlag_0_31(extiLine);
}

std_err_t mcu_gpio_exti_clear(mcu_gpio_num_t num) { 
	uint32_t extiLine = _gpio_exti_line(num);
	LL_EXTI_ClearFlag_0_31(extiLine);
	return STD_ERR_OK;
}
